using IQIGame.Onigao.Framework;
using NetProtocol.POD;
using System.Collections.Generic;

namespace IQIGame.Onigao.Logic
{
    public class ServiceDelayManager
    {
        private List<DelayService> _delayServiceList = new List<DelayService>();
        private List<DelayService> _tempRemoveServices = new List<DelayService>();

        /// <summary>
        /// 添加后台延迟服务
        /// 意思是程序自行管理的服务，触发时再检查条件
        /// </summary>
        /// <param name="player"></param>
        /// <param name="serviceCid"></param>
        public void AddBackendService(LevelPlayer player, LevelEntity entity, int serviceCid, DelayServiceCondition conditionState, int delayFrame, bool needSave)
        {
            if (serviceCid == 0)
            {
                LogicLog.LogWarning("添加后台延迟服务失败，服务id为0");
                return;
            }
            bool hasCfg = entity.region.gameplayConfig.services.TryGetValue(serviceCid, out var cfg);
#if UNITY_EDITOR
            if (!hasCfg)
            {
                LogicLog.LogError($"关卡配置里找不到服务. Service id: {serviceCid}");
                return;
            }
#endif
            //如果后台延迟服务被保存（如气泡结束服务，镜头移动结束服务等），那么可能会发生服务未执行前玩家离开关卡，重新进入关卡时实体已被重置，但服务依然会执行的情况。
            //如果该服务执行的结果要依赖被重置的某实体的特定状态（位置等），则可能会出现bug。
            //但如果不保存，也可能会出现关键服务中断的情况，此类情况需策划自行规避。
            //策划认为基本都不会保存，所以需要保存的时候提醒策划注意。
            if (needSave)
            {
                LogicLog.LogWarning("添加了【需要存档】的后台延迟服务（气泡，镜头动画等结束服务），请确保它的执行结果不会和会重置的实体发生依赖关系。");
            }
            for (int i = 0; i < cfg.serviceUnits.Count; i++)
            {
                var unitConfig = cfg.serviceUnits[i];
                LevelServiceUnit unit = ClassPool.Get<LevelServiceUnit>();
                unit.Init(cfg.id, i, unitConfig, entity);
                AddDelayServiceByDelayFrame(delayFrame, player, unit, conditionState, true, needSave);
            }
        }

        /// <summary>
        /// 添加延迟服务，延迟时间根据服务配置决定，条件事先计算好，执行时条件是否满足的状态不再变化
        /// </summary>
        /// <param name="player"></param>
        /// <param name="service"></param>
        /// <param name="isConditionOK"></param>
        /// <param name="autoRelease"></param>
        public void AddDelayService(LevelPlayer player, LevelServiceUnit service, DelayServiceCondition conditionState, bool autoRelease, bool needSave)
        {
            AddDelayServiceByDelayFrame(LgLevelConvertUtil.MillisecToFrame(service.delay), player, service, conditionState, autoRelease, needSave);
        }

        /// <summary>
        /// 添加延迟服务，逻辑同AddDelayService，只是延迟时间由外部指定，单位是帧
        /// </summary>
        /// <param name="delayFrame"></param>
        /// <param name="player"></param>
        /// <param name="service"></param>
        /// <param name="isConditionOK"></param>
        /// <param name="autoRelease"></param>
        public void AddDelayServiceByDelayFrame(int delayFrame, LevelPlayer player, LevelServiceUnit serviceUnit, DelayServiceCondition conditionState, bool autoRelease, bool needSave)
        {
            DelayService delayService = ClassPool.Get<DelayService>();
            delayService.Init(delayFrame, player, serviceUnit, conditionState, autoRelease, needSave);

            _delayServiceList.Add(delayService);
        }

        public void LogicOnUpdate()
        {
            for (int i = 0; i < _delayServiceList.Count; i++)
            {
                DelayService service = _delayServiceList[i];
                service.LogicOnUpdate();
                if (service.isExpired)
                {
                    _tempRemoveServices.Add(service);
                }
            }
            for (int i = 0; i < _tempRemoveServices.Count; i++)
            {
                DelayService service = _tempRemoveServices[i];
                _delayServiceList.Remove(service);
                ClassPool.Put(service);
            }
            _tempRemoveServices.Clear();
        }

        public void CancelDelayByEntity(int entityId)
        {
            for (int i = 0; i < _delayServiceList.Count; i++)
            {
                DelayService service = _delayServiceList[i];
                if (service.entity != null && service.entity.id == entityId)
                {
                    service.isExpired = true;
                }
            }
        }

        public void CancelDelayByEntityTagOrMcid(int tagOrMcid)
        {
            for (int i = 0; i < _delayServiceList.Count; i++)
            {
                DelayService service = _delayServiceList[i];
                bool useTag = LgLevelEntityUtil.IsTag(tagOrMcid);
                if (service.entity != null && (
                    (!useTag && service.entity.entityConfig.id == tagOrMcid)
                    || (useTag && service.entity.tags.Contains((ushort)tagOrMcid))
                ))
                {
                    service.isExpired = true;
                }
            }
        }

        public void CancelDelayByServiceCid(int serviceCid)
        {
            for (int i = 0; i < _delayServiceList.Count; i++)
            {
                DelayService service = _delayServiceList[i];
                if (service.serviceUnit.serviceId == serviceCid)
                {
                    service.isExpired = true;
                }
            }
        }

        public void CancelDelayByPlayer(LevelPlayer player)
        {
            for (int i = 0; i < _delayServiceList.Count; i++)
            {
                DelayService service = _delayServiceList[i];
                if (service.player != null && service.player == player)
                {
                    service.isExpired = true;
                }
            }
        }

        public List<DelayServicePOD> GeneratePOD()
        {
            if (_delayServiceList.Count > 0)
            {
                List<DelayServicePOD> list = new List<DelayServicePOD>();
                for (int i = 0; i < _delayServiceList.Count; i++)
                {
                    DelayService service = _delayServiceList[i];
                    if (service.needSave && !service.isExpired)
                    {
                        list.Add(service.GeneratePOD() as DelayServicePOD);
                    }
                }
                return list.Count > 0 ? list : null;
            }
            return null;
        }

        public void Dispose()
        {
            for (int i = 0; i < _delayServiceList.Count; i++)
            {
                DelayService service = _delayServiceList[i];
                ClassPool.Put(service);
            }
            _delayServiceList.Clear();
            _tempRemoveServices.Clear();
        }
    }
}
